#include "Neuron.h"

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  Neuron
 * Description:  Constrói um neurônio passando o nº de variáveis de entrada, a função de
 *               ativação e o aprendizado
 *               Os pesos do neurônio serão calculados randomicamente se csv for nulo
 *--------------------------------------------------------------------------------------
 */
Neuron::Neuron(uint nInVars, ActivationPtr activation, LearningPtr learning, CSVFilePtr csv)
{
    this->nInVars = nInVars;
    this->activation = activation;
    this->learning = learning;

    if(csv.get() == NULL)
    {
        weights.resize(nInVars + 1);
        reset(); // Seta os pesos inicias com valores aleatórios
    }
    else
    {
        csv->readV(weights, nInVars + 1); // Seta os pesos
    }
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  resetWeights
 * Description:  Reseta os pesos do neurônio
 *--------------------------------------------------------------------------------------
 */
void Neuron::reset()
{
    for(uint i = 0; i < weights.size(); i++)
        weights[i] = getRandom();
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  getRandom
 * Description:  Retorna um valor aleatório baixo entre 0 e 0.1
 *--------------------------------------------------------------------------------------
 */
double Neuron::getRandom()
{
    return rand() * 0.1 / (double) (RAND_MAX);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  getOutput
 * Description:  Retorna o output do neurônio dada uma entrada
 *--------------------------------------------------------------------------------------
 */
double Neuron::getOutput(vdoublePtr input)
{
    double sum = 0;

    // Realiza a função de propagação (somatória das entradas com seus pesos)
    for(uint i = 0; i < input->size(); i++)
        sum += weights[i] * input->at(i);
    sum += weights[nInVars];

    // Saída não ativada
    nonActivatedOutput = sum;

    // Saída ativada
    activatedOutput = activation->calc(sum);

    return activatedOutput;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  getError
 * Description:  Retorna o erro do neurônio passando a saída esperada
 *--------------------------------------------------------------------------------------
 */
double Neuron::getError(double expected)
{
    error = learning->error(expected, activatedOutput, nonActivatedOutput);
    return error;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  getError
 * Description:  Retorna o error do neurônio passando os erros da próxima camada e seus pesos
 *--------------------------------------------------------------------------------------
 */
double Neuron::getError(vdoublePtr nextErrors, vdoublePtr nextWeights)
{
    error = learning->error(nextErrors, nextWeights, nonActivatedOutput);
    return error;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  update
 * Description:  Atualiza seus pesos com base no erro obtido
 *--------------------------------------------------------------------------------------
 */
void Neuron::update(vdoublePtr input)
{
    // Chama o aprendizado para cada peso do neurônio
    for(uint i = 0; i < nInVars; i++)
        weights[i] += learning->delta(input->at(i), error);
    weights[nInVars] += learning->delta(1, error);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Neuron
 *      Method:  printWeights
 * Description:  Imprime os pesos
 *--------------------------------------------------------------------------------------
 */
void Neuron::print()
{
    for(uint i = 0; i <= nInVars; i++)
        cout << weights[i] << " ";
}

